﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Layers;

namespace Knight
{
    public partial class Knight : UserControl
    {
        InteractiveBuffer buffers;
        Rectangle rect;
        int chessWidth = -1;

        SolidBrush whiteBrush;
        SolidBrush blackBrush;
        SolidBrush lightSalmonBrush;
        SolidBrush lightSkyBlue;

        Thread thread;
        int sleepTime;
        bool haveResult = false;
        bool step = false;
        System.Windows.Forms.Timer refreshTimer;

        // Algorithm
        static int[][] chess;
        static Point[] nextMoving;
        static Point currentPos;
        static int currentMove = 1;


        // Image
        Image knightIcon;
        Font font;

        public Knight()
        {
            InitializeComponent();

            // Init refresh timer

            refreshTimer = new System.Windows.Forms.Timer();
            refreshTimer.Interval = 600;
            refreshTimer.Tick += new EventHandler(refreshTimer_Tick);
        }

        protected override void OnLoad(EventArgs e)
        {
            /* Init variable */

            this.chessWidth = 8;
            // Init chess, first init only
            if(Knight.chess == null)
                this.InitChess();
            if (Knight.nextMoving == null)
            {
                this.InitNextMoving();
            }
            if (Knight.currentPos == null)
            {
                Knight.currentPos = new Point();
            }

            // Control style
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;

            // For drawing
            this.whiteBrush = new SolidBrush(Color.White);
            this.blackBrush = new SolidBrush(Color.Black);
            this.lightSalmonBrush = new SolidBrush(Color.LightSalmon);
            this.lightSkyBlue = new SolidBrush(Color.LightSkyBlue);

            this.rect = new Rectangle(0, 0, this.Width, this.Height);
            this.buffers = new InteractiveBuffer(this, rect);
            this.buffers.AddLayer("background");
            this.buffers.AddLayer("top");
            this.buffers["background"].Func += new RepresetBuffer.DrawBufferHandle(DrawChessBackground);
            this.buffers["top"].Func += new RepresetBuffer.DrawBufferHandle(DrawKnight);

            this.sleepTime = 1000; // Default sleep time

            // Init image

            string filePath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
            filePath += "\\imgs\\knight.png";
            this.knightIcon = Bitmap.FromFile(filePath);
            this.font = new Font(this.Font.Name, 15.0F ,FontStyle.Bold);
            
        }

        void refreshTimer_Tick(object sender, EventArgs e)
        {
            this.Refresh();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (this.haveResult == true)
                return;
            this.buffers.Render(e.Graphics);
        }

        protected override void OnResize(EventArgs e)
        {
            // Recreate buffer
            if (this.buffers != null)
            {
                this.rect = new Rectangle(0, 0, this.Width, this.Height);
                this.buffers = new InteractiveBuffer(this, rect);
                this.buffers.AddLayer("background");
                this.buffers.AddLayer("top");
                this.buffers["background"].Func += new RepresetBuffer.DrawBufferHandle(DrawChessBackground);
                this.buffers["top"].Func += new RepresetBuffer.DrawBufferHandle(DrawKnight);
                this.buffers.RefreshContext();
                this.Refresh();
            }
        }

        private void DrawChessBackground(Graphics g, Rectangle r)
        {
            int cellWidth = this.Width / this.chessWidth;
            for (int i = 0; i < this.chessWidth; i++)
            {
                for (int j = 0; j < this.chessWidth; j++)
                {
                    if (Knight.chess[i][j] >= 1) // Knight pass point(i,j)
                    {
                        if ((i + j) % 2 == 0)
                        {
                            g.FillRectangle(this.lightSalmonBrush, new Rectangle(i * cellWidth, j * cellWidth, cellWidth, cellWidth));
                        }
                        else
                        {
                            g.FillRectangle(this.lightSkyBlue, new Rectangle(i * cellWidth, j * cellWidth, cellWidth, cellWidth));
                        }

                        // drawnumber

                        int _number = Knight.chess[i][j];
                        // Point _p = new Point();

                        g.DrawString(_number.ToString(), font, this.blackBrush, new Rectangle(i * cellWidth, j * cellWidth, cellWidth, cellWidth));
                        
                    }
                    else
                    {
                        if ((i + j) % 2 == 0)
                        {
                            g.FillRectangle(this.whiteBrush, new Rectangle(i * cellWidth, j * cellWidth, cellWidth, cellWidth));
                        }
                        else
                        {
                            g.FillRectangle(this.blackBrush, new Rectangle(i * cellWidth, j * cellWidth, cellWidth, cellWidth));
                        }
                    }

                }
            }
        }

        private void DrawKnight(Graphics g, Rectangle r)
        {
            int cellWidth = this.Width / this.chessWidth;
            g.DrawImage(this.knightIcon, new Rectangle(currentPos.X * cellWidth + 2, currentPos.Y * cellWidth + 2, cellWidth - 4, cellWidth - 4));
        }

        private delegate void MakeRefreshCallBack();
        private void MakeRefresh()
        {
            if (this.InvokeRequired)
            {
                MakeRefreshCallBack d = new MakeRefreshCallBack(MakeRefresh);
                this.Invoke(d);
            }
            else
            {
                this.Refresh();
            }
        }

        /// <summary>
        /// Force control refresh frequently
        /// </summary>
        public void ForceRefresh()
        {
            this.refreshTimer.Start();

        }

        public void StartSimulate()
        {

            if (thread == null)
            {
                thread = new Thread(new ThreadStart(this.Simulate));
                RandomFirstPoint();
                this.refreshTimer.Start();
                thread.Start();
            }

        }

        public void ResetSimulate()
        {
            this.AbortSimulate();
            this.InitChess();
            this.InitNextMoving();
            this.Refresh();
        }

        public void AbortSimulate()
        {
            if (thread != null)
            {
                thread.Abort();
                this.refreshTimer.Stop();
                this.haveResult = false;
                thread = null;
            }
        }

        public void Step()
        {
            this.step = true;
        }

        private void RandomFirstPoint()
        {
            Random random = new Random((int)DateTime.Now.Ticks);
            int x = random.Next(this.chessWidth);
            int y = random.Next(this.chessWidth);
            Knight.chess[x][y] = 1;
            Knight.currentMove++;
            Knight.currentPos = new Point(x,y);
        }

        private void Simulate()
        {
            while (true)
            {
                if (this.IsPatrolComplete())
                {
                    this.MakeRefresh();
                    this.haveResult = true;
                    break;
                }
                this.NextPoints();
                // User request steping in simulation
                if (this.step == true)
                {
                    this.step = false;
                    while (this.step == false)
                    {
                        // Stop thread
                        Thread.Sleep(100);
                    }
                }
                else
                {
                    // Sleep for normal simulation speed
                    if (this.sleepTime != 0)
                        Thread.Sleep(this.sleepTime);
                }
            }
            // Reset stop value
        }

        private bool IsPatrolComplete()
        {
            for (int i = 0; i < this.chessWidth; i++)
            {
                for (int j = 0; j < this.chessWidth; j++)
                {
                    if (Knight.chess[i][j] == 0)
                        return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Calculate next point and replace current pos with that new point
        /// It's heuristic.
        /// </summary>
        private void NextPoints()
        {
            Dictionary<int, int> indexDictionary = new Dictionary<int, int>();

            // get next moving for current knight
            Knight.nextMoving = GetNextPoints(currentPos);

            // Find min next point
            for (int i = 0; i < this.chessWidth; i++)
            {
                if (Knight.nextMoving[i].X < 0 || Knight.nextMoving[i].Y < 0 || 
                    Knight.nextMoving[i].X >= this.chessWidth || Knight.nextMoving[i].Y >= this.chessWidth ||
                    Knight.chess[nextMoving[i].X][nextMoving[i].Y] >= 1)
                    continue;
                // Find next point possible moving(s)
                var temp = GetNextPoints(Knight.nextMoving[i]);
                int availablePoints = CountValidatePoint(temp);
                // Add to dictory
                indexDictionary.Add(i, availablePoints);
            }

            // Compare and find suitable point
            int newIndex = this.ChoicePoint(indexDictionary);
            // Change pos to new postion
            Knight.currentPos = Knight.nextMoving[newIndex];
            Knight.chess[currentPos.X][currentPos.Y] = Knight.currentMove;
            currentMove++;
        }

        private Point[] GetNextPoints(Point startingPos)
        {
            List<Point> points = new List<Point>();

            points.Add(new Point(startingPos.X + 1, startingPos.Y - 2));
            points.Add(new Point(startingPos.X + 2, startingPos.Y - 1));
            points.Add(new Point(startingPos.X + 2, startingPos.Y + 1));
            points.Add(new Point(startingPos.X + 1, startingPos.Y + 2));
            points.Add(new Point(startingPos.X - 1, startingPos.Y + 2));
            points.Add(new Point(startingPos.X - 2, startingPos.Y + 1));
            points.Add(new Point(startingPos.X - 2, startingPos.Y - 1));
            points.Add(new Point(startingPos.X - 1, startingPos.Y - 2));

            return points.ToArray();
        }

        private int CountValidatePoint(Point[] points)
        {
            int counter = 0;
            for (int i = 0; i < points.Length; i++)
            {
                if (points[i].X < 0 || points[i].Y < 0 || points[i].X >= this.chessWidth || points[i].Y >= this.chessWidth)
                    continue;
                if (Knight.chess[points[i].X][points[i].Y] == 0)
                    counter++;
            }
            return counter;
        }

        private int ChoicePoint(Dictionary<int, int> indexDictionary)
        {
            // Init local variable

            int min = indexDictionary.First().Value;

            List<int> mins = new List<int>();

            // Find min value

            foreach (var item in indexDictionary)
            {
                if (min > item.Value)
                {
                    min = item.Value;
                }
            }

            // Find min values

            foreach (var item in indexDictionary)
            {
                if (min == item.Value)
                {
                    mins.Add(item.Key);
                }
            }

            if (mins.Count == 1) // happily, one point only
            {
                return mins[0];
            }
            else // find furthest point
            {
                int furthestIndex = 0;
                int furthestValue = 0;
                int haltWidth = this.chessWidth/2;
                foreach (int index in mins)
                {
                    int i = haltWidth - Knight.nextMoving[index].X;
                    int j = haltWidth - Knight.nextMoving[index].Y;
                    int z = i * i + j * j;
                    if (furthestValue <= z)
                    {
                        furthestValue = z;
                        furthestIndex = index;
                    }
                }
                return furthestIndex;
            }
        }

        /// <summary>
        /// Must call function after init chessWidth
        /// </summary>
        private void InitChess()
        {
            Knight.chess = new int[this.chessWidth][];
            for (int i = 0; i < this.chessWidth; i++)
            {
                Knight.chess[i] = new int[this.chessWidth];
            }
        }

        private void InitNextMoving()
        {
            Knight.nextMoving = new Point[this.chessWidth];
            for (int i = 0; i < this.chessWidth; i++)
            {
                Knight.nextMoving[i] = new Point();
            }
        }


        public bool IsCompleted
        {
            get
            {
                return this.haveResult;
            }
        }

        /// <summary>
        /// Set simulate speed
        /// </summary>
        public int Speed
        {
            set
            {
                this.sleepTime = value;
            }
        }

        
    }
}
